package com.open.source.javassist;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;

/**
 * @author ZonLen since on 2022/3/24 上午12:14
 */
public final class AnnotationMaker {

  private Class<?> annotationClass;

  private final Map<String, Object> annotationAttrs = new HashMap<>();

  private final ConstPool constPool;

  private AnnotationMaker(ConstPool constPool) {
    this.constPool = constPool;
  }

  public static AnnotationMaker instance(ConstPool constPool) {
    return new AnnotationMaker(constPool);
  }

  public AnnotationMaker annotationClass(Class<?> annotationClass) {
    this.annotationClass = annotationClass;
    return this;
  }

  public AnnotationMaker annotationAttr(String annotationMethod, Object val) {
    this.annotationAttrs.put(annotationMethod, val);
    return this;
  }

  public AnnotationMaker annotationAttr(java.lang.annotation.Annotation annotation) {
    for (Method method : annotationClass.getMethods()) {
      final Class<?>[] parameterTypes = method.getParameterTypes();
      if (parameterTypes.length == 0 &&
          !Arrays.asList("annotationType", "toString", "hashCode").contains(method.getName())) {
        final Object val;
        try {
          val = method.invoke(annotation);
        } catch (IllegalAccessException | InvocationTargetException e) {
          throw new RuntimeException(e);
        }
        if (val.getClass().isArray()) {
          continue;
        }
        annotationAttrs.put(method.getName(), val);
      }
    }
    return this;
  }

  public AnnotationsAttribute buildAnnotation() {
    AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool,
        AnnotationsAttribute.visibleTag);
    //创建要添加的注解
    Annotation annotation = new Annotation(annotationClass.getCanonicalName(), constPool);
    //设置注解中的属性和值
    annotationAttrs.forEach(
        (k, v) -> annotation.addMemberValue(k, new StringMemberValue(v.toString(), constPool)));
    //把这个注解放到一个AnnotationsAttribute对象里面
    annotationsAttribute.addAnnotation(annotation);
    return annotationsAttribute;
  }

}
